package cn.wizzer.framework.util;

import cn.wizzer.framework.base.Result;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Consumer;


/**
 * aq
 */
public class HttpUtil {

    private static final Log log = Logs.get();

    private static final CloseableHttpClient httpclient = HttpClients.createDefault();

    public static <K, V> Map<K, V> createMap(Object... args) {
        TreeMap<K, V> map = new TreeMap<>();
        for (int i = 0; i < args.length; i += 2) {
            map.put((K) args[i], (V) args[i + 1]);
        }
        return map;
    }

    private HttpUtil() {
    }

    /**
     * 发送HttpGet请求
     *
     * @param url
     * @return
     */
    public static String sendGet(String url) {
        String result = null;
        HttpGet httpget = new HttpGet(url);
        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(httpget);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                result = EntityUtils.toString(entity);
            }
        } catch (ParseException | IOException e) {
            e.printStackTrace();
            log.error("请求异常" + e.getMessage());
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 发送HttpPost请求，参数为map
     *
     * @param url
     * @param map
     * @return
     */
    public static String sendPost(String url, Map<String, String> map) {
        String result = null;
        try {
            List<NameValuePair> formparams = new ArrayList<NameValuePair>();
            if (map != null && 0 < map.keySet().size()) {
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
            HttpPost httppost = new HttpPost(url);
            RequestConfig config = RequestConfig.custom().setSocketTimeout(3000).setConnectionRequestTimeout(3000).setConnectTimeout(3000).build();
            httppost.setConfig(config);
            httppost.setEntity(entity);
            CloseableHttpResponse response = null;
            response = httpclient.execute(httppost);
            HttpEntity entity1 = response.getEntity();
            result = EntityUtils.toString(entity1);
        } catch (ParseException | IOException e) {
            e.printStackTrace();
            log.error("请求异常" + e.getMessage());
        }
        return result;
    }

    public static Request createRequest() {
        return new Request();
    }

    public static Request.Callback createCallBack(Consumer<Request> consumer) {
        return new Request.Callback(consumer);
    }


    /**
     * 发送不带参数的HttpPost请求
     *
     * @param url
     * @return
     */
    public static String sendPost(String url) {
        String result = null;
        HttpPost httppost = new HttpPost(url);
        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
        } catch (ParseException | IOException e) {
            e.printStackTrace();
            log.error("请求异常" + e.getMessage());
        }
        return result;
    }

    public static class Response {
        private static final Header[] EMPTY_HEADER = new Header[0];
        private ProtocolVersion pVersion;
        private Locale locale;
        private StatusLine statusLine;
        private CloseableHttpResponse response;
        private Header[] headers;
        private String body;
        private HttpEntity entity;

        Response(CloseableHttpResponse response) {

            if (null == response) {
                throw new NullPointerException("no response");
            }
            this.response = response;

            entity = response.getEntity();

            getBody();

            headers = response.getAllHeaders();

            statusLine = response.getStatusLine();

            locale = response.getLocale();

            pVersion = response.getProtocolVersion();

            close();
        }

        public String getBody() {
            if (null == body) {
                try {
                    // 转换字符串为utf8编码
                    body = EntityUtils.toString(entity, HTTP.UTF_8);
                } catch (ParseException | IOException e) {
                    e.printStackTrace();
                }
            }
            return body;
        }

        public HttpEntity getEntity() {
            return entity;
        }

        public ProtocolVersion getpVersion() {
            return pVersion;
        }

        public Locale getLocale() {
            return locale;
        }

        public StatusLine getStatusLine() {
            return statusLine;
        }

        public Header[] getHeaders() {
            return response.getAllHeaders();
        }

        public Header[] getHeader(String name) {
            List<Header> headersFound = null;

            for (int i = 0; i < this.headers.length; ++i) {
                Header header = this.headers[i];
                if (header.getName().equalsIgnoreCase(name)) {
                    if (headersFound == null) {
                        headersFound = new ArrayList();
                    }
                    headersFound.add(header);
                }
            }

            return headersFound != null ? (Header[]) headersFound.toArray(new Header[headersFound.size()]) : EMPTY_HEADER;
        }

        @Override
        public void finalize() {
            close();
        }

        public void close() {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static class Request {

        private Callback callback;
        private Integer timeOut;
        private String url;
        private Map<String, Object> param;
        private Response response;
        private Map<String, String> header;
        private HTTP_METHOD method;

        public Request setCallback(Callback callback) {
            this.callback = callback;
            return this;
        }

        public String getUrl() {
            return url;
        }

        public Request setTimeOut(Integer timeOut) {
            this.timeOut = timeOut;
            return this;
        }

        public Request setUrl(String url) {
            this.url = url;
            return this;
        }

        public Map<String, Object> getParam() {
            return param;
        }

        public Request setParam(Map<String, Object> param) {
            this.param = param;
            return this;
        }

        public Request setParam(String key, Object value) {
            if (this.param == null) {
                this.param = new HashMap<>(8);
            }
            this.param.put(key, value);
            return this;
        }

        public Map<String, String> getHeader() {
            return header;
        }

        public Request setHeader(Map<String, String> header) {
            this.header = header;
            return this;
        }

        public Request setHeader(String key, String value) {
            if (this.header == null) {
                this.header = new HashMap<>(8);
            }
            this.header.put(key, value);
            return this;
        }

        public Response send() throws IOException {
            this.sendPost();
            return this.response;
        }

        public Response send(HTTP_METHOD method) throws IOException {
            switch (this.method = method) {
                case POST_JSON:
                case POST:
                    return this.sendPost();
                case GET:
                    return this.sendGet();
            }
            if (response == null) {
                Result.breakWith(method + " has not yet implemented the class");
            }
            return this.response;
        }

        private Response sendGet() throws IOException {
            CloseableHttpResponse closeableHttpResponse = null;
            HttpGet httpGet = null;
            try {

                if (null != param && param.keySet().size() > 0) {
                    StringBuilder uri = new StringBuilder(url);
                    if (url.contains("?")) {
                        uri.append("&");
                    } else {
                        uri.append("?");
                    }
                    param.forEach((key, val) -> {
                        uri.append(key);
                        uri.append("=");
                        try {
                            uri.append(URLEncoder.encode(String.valueOf(val), "UTF-8"));
                        } catch (UnsupportedEncodingException e) {
                            log.error(e);
                        }
                        uri.append("&");
                    });
                    uri.substring(0, uri.length() - 1);
                    httpGet = new HttpGet(uri.toString());
                } else {
                    httpGet = new HttpGet(url);
                }
                appendConfig(httpGet);
                appendHeader(httpGet);
                closeableHttpResponse = httpclient.execute(httpGet);
                Response response = new Response(closeableHttpResponse);
                this.response = response;
                return response;
            } finally {
                if (closeableHttpResponse != null) {
                    try {
                        httpGet.abort();
                        closeableHttpResponse.close();
                    } catch (IOException e) {
                        log.error(e);
                    }
                }
            }
        }

        private void appendHeader(HttpRequest req) {
            if (null != header && header.keySet().size() > 0) {
                Header[] headers = new Header[header.keySet().size()];
                Header hd;
                int i = 0;
                for (Map.Entry<String, String> entry : header.entrySet()) {
                    hd = new BasicHeader(entry.getKey(), entry.getValue());
                    headers[i++] = hd;
                }
                req.setHeaders(headers);
            }
        }

        /**
         * 发送HttpPost请求，参数为map
         *
         * @return
         */
        private Response sendPost() throws IOException {
            CloseableHttpResponse closeableHttpResponse = null;
            HttpPost httpPost = new HttpPost(url);
            try {
                appendConfig(httpPost);
                if (null != param && param.keySet().size() > 0) {
                    if (this.method == HTTP_METHOD.POST_JSON) {
                        StringEntity entity = new StringEntity(new String(JSONUtil.toJSONString(param).getBytes(), StandardCharsets.UTF_8));
                        entity.setContentEncoding("UTF-8");
                        entity.setContentType("application/json");
                        httpPost.setEntity(entity);
                    } else {
                        List<NameValuePair> params = new ArrayList<NameValuePair>(param.size());
                        for (Map.Entry entry : param.entrySet()) {
                            params.add(new BasicNameValuePair(entry.getKey() == null ? null : entry.getKey().toString(), entry.getValue() == null ? null : entry.getValue().toString()));
                        }
                        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, Consts.UTF_8);
                        httpPost.setEntity(entity);
                    }
                }
                appendHeader(httpPost);
                closeableHttpResponse = httpclient.execute(httpPost);
                Response response = new Response(closeableHttpResponse);
                this.response = response;
                return response;
            } finally {
                if (closeableHttpResponse != null) {
                    try {
                        httpPost.abort();
                        closeableHttpResponse.close();
                    } catch (IOException e) {
                        log.error(e);
                    }
                }
            }
        }

        private void appendConfig(HttpRequestBase req) {
            if (this.timeOut != null) {
                RequestConfig config = RequestConfig.custom().setConnectTimeout(timeOut).setSocketTimeout(timeOut).setConnectionRequestTimeout(timeOut).build();
                req.setConfig(config);
            } else {
                RequestConfig config = RequestConfig.custom().setSocketTimeout(60 * 1000).setConnectionRequestTimeout(3000).setConnectTimeout(3000).build();
                req.setConfig(config);
            }
        }

        public void asyncSend(HTTP_METHOD method) {
            ThreadUtil.execute(() -> {
                try {
                    this.send(method);
                    if (callback != null) {
                        callback.run(this);
                    }
                } catch (IOException e) {
                    log.error(e);
                }
            });
        }

        public Response getResponse() {
            return response;
        }

        public static class Callback {

            private Consumer consumer;

            Callback(Consumer<Request> consumer) {
                this.consumer = consumer;
            }

            public void run(Request request) {
                consumer.accept(request);
            }

        }

    }

    public enum HTTP_METHOD {
        POST("post"),
        POST_JSON("post_json"),
        GET("get"),
        OPTIONS("options"),
        PUT("put"),
        DELETE("delete");
        private String name;

        HTTP_METHOD(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }

}
